home *** CD-ROM | disk | FTP | other *** search
- #pragma inline
-
- /*
-
- RM.C Copyright (C) 1988 Mark Adler Pasadena, CA
- All rights reserved.
-
- Version history -
-
- 1.0 4 Jun 1988 First public version
- 1.1 4 Nov 1988 Make rm as fast as del (use FCB delete)
- 1.2 18 Nov 1988 Fixed for Turbo C 2.0 (label needs statement)
- 1.3 3 Feb 1989 Display files and directories in lower case
- 1.4 21 Oct 1989 Catch dangling options and ignore command
-
-
- RM is an extended delete command. It can delete all the files in a
- directory and its subdirectories, etc. and remove all the directories.
- Alternatively, it can delete all the files in any directories that match
- the specified pattern. Since RM is a more powerful delete command, it
- can be that much more dangerous. So the following warning is in order:
-
- WARNING: RM IS A VERY DESTRUCTIVE COMMAND. It will NOT ask for
- verification of a particularily destructive argument like the DOS DEL
- command does. What you ask it is what it does. BE CAREFUL.
-
- Normally, RM acts like the DEL command, deleting any files that match
- the pattern, except that it does not verify the argument *.*---it just
- does it. For example:
-
- rm *.bak
-
- will delete all the *.bak files in the current directory. The command:
-
- rm *.*
-
- will delete all the files in the current directory without asking first.
-
- RM can also delete the contents of subdirectories and then remove the
- subdirectories. The option for this is "/S". For example:
-
- rm/s \tex
-
- will delete all the files and subdirectories contained in \tex and
- finally remove the directory \tex. The command:
-
- rm/s \tex\*.*
-
- will do the same thing, but not remove the directory tex, just
- everything in it.
-
- Alternatively, RM can delete any files that match the pattern in all
- subdirectories. The option for this is "/F":
-
- rm/f \*.bak
-
- will delete all *.bak files on the current drive, no matter where they
- are. No subdirectories will be removed.
-
- If you think that's dangerous, RM can also remove hidden, system, or
- read-only files if asked to. The options are "/H", "/Y", and "/R".
- For example:
-
- rm/shyr \*.* DON'T EVEN THINK OF TYPING THIS COMMAND.
-
- will remove every single file and directory on the current drive,
- including the system---leaving only the drive label, a lot of empty
- space, and a user in a state of panic.
-
- RM can take multiple arguments, processing each in turn. For example:
-
- rm *.obj *.lst *.map
-
- will delete all the files specified.
-
- RM does have one option that makes it a little less dangerous; "/P".
- This option causes RM to prompt you for every file or directory it tries
- to get rid of. For example:
-
- rm/p *.*
-
- might ask you:
-
- Delete file test.c (y/n)? _
-
- or
-
- Remove directory \tmp (y/n)? _
-
- In either case, you must respond with either an upper or lower case "Y"
- or "N". Any other response is ignored.
-
- When it is done, RM will tell you how many files it deleted and how many
- directories it removed. It might say, for example:
-
- 13 files deleted
-
- or
-
- 129 files deleted and 4 directories removed
-
- If RM is, for some reason, unable to delete a file or remove a
- directory, it will say:
-
- Could not delete file test.c
-
- or
-
- Could not remove directory \tmp - part of current path
-
- or
-
- Could not remove directory \tmp - unable to empty it
-
- It should not be possible to get the first error. The second error
- occurs when the directory RM tried to remove is part of the current path
- for that drive. DOS does not allow pulling the rug out from under your
- feet by removing a directory you are in. The remedy is to change out of
- the directory you wish to remove and repeating the RM command.
-
- The third error can happen when RM tries to remove a directory with
- read-only, hidden, or system files in it, and no options were specified
- to allow RM to remove those. The remedy is to repeat the RM command
- with the necessary options to delete the files desired.
-
- RM takes options that are preceded by a slash (/). The command line is
- read from left to right, processing options and file/path names as they
- appear. However, options that are appended to a file/path name take
- effect before that name is processed. For example, this command would
- do the same thing as "rm/p *.c":
-
- rm *.c/p
-
- But the command "rm *.c /p" (notice the extra space before the /p) would
- not do the same thing. In this case, RM notices the "dangling" option
- before it does anything and simply ignores the entire command for
- safety, printing:
-
- Dangling option---entire command ignored
-
- Since there are options to change the defaults along a command line,
- there are options to change them back for processing the next thing
- on the command line. All the options are listed here:
-
- /P - Prompt for each deletion or removal.
- /Q - Quiet---don't prompt (default).
- /S - Remove matching subdirectories and all of their contents.
- /N - Ignore subdirectories (default).
- /H - Remove hidden files.
- /V - Visible---ignore hidden files (default).
- /Y - Remove system files.
- /T - Typical---ignore system files (default).
- /R - Remove read-only files!
- /W - Only delete writable files (default).
- /F - Remove all files that match pattern, and no subdirs.
- /A - Remove all files in subdirectories, period (default).
- /? - Display version number and list of options.
-
- For example, the command:
-
- rm /p *.lst /q *.obj
-
- will ask before deleting each file that matches *.lst, and then delete
- all the files that match *.obj without asking.
-
- Options can be combined with or without additional slashes. For
- example, these commands do the same thing:
-
- rm *.lst/pr
- rm/p/r *.lst
- rm /p /r *.lst
- rm /p *.lst/r
-
- Once again---BE CAREFUL WITH THIS COMMAND. Thanks to Kurt Schmidt for
- suggesting that RM be changed to notice a dangling option.
-
- */
-
-
- typedef unsigned long ulong;
-
-
- /* Option flags, assigned to default values */
- char ask = 0, /* Prompt for each deletion */
- sub = 0, /* Include subdirectories */
- att = 0, /* Attribute for hidden, system */
- rof = 0, /* Remove read-only files also */
- fin = 0; /* Find---use same name in subdirs */
-
-
- ulong fls, drs; /* Number of files, directories removed */
-
-
- /* Structure for fnd1st(), fndnxt() */
- struct find {
- char rsvd[21]; /* What DOS needs to keep track of find */
- char attr; /* File attribute */
- unsigned time; /* Time stamp */
- unsigned date; /* Date stamp */
- ulong size; /* File size in bytes */
- char name[13]; /* FIle name as a zero terminated string */
- };
-
-
- /* Send character 'c' to stdout */
- void pputc(char c)
- {
- asm mov DL,c
- asm mov AH,2
- asm int 21h
- }
-
-
- /* Send string 's' to stdout */
- void pputs(char *s)
- {
- asm mov SI,s
- asm cld
- plp:
- asm lodsb
- asm test AL,AL
- asm jz pfin
- asm mov DL,AL
- asm mov AH,2
- asm int 21h
- asm jmp short plp
- pfin: ;
- }
-
-
- /* Convert the string s from upper case to lower case */
- char *strlow(char *s)
- {
- asm mov AX,DS
- asm mov ES,AX
- asm cld
- asm mov SI,s
- asm lea DI,[SI-1]
- llp:
- asm inc DI
- llpnoi:
- asm lodsb
- asm test AL,AL
- asm jz lfin
- asm cmp AL,'A'
- asm jb llp
- asm cmp AL,'Z'
- asm ja llp
- asm add AL,'a'-'A'
- asm stosb
- asm jmp short llpnoi
- lfin: ;
- return s;
- }
-
-
- /* Copy string s to string d, return end of d */
- char *scpy(char *d, char *s)
- {
- asm mov SI,s
- asm mov DI,d
- asm cld
- asm mov AX,DS
- asm mov ES,AX
- slp:
- asm lodsb
- asm stosb
- asm test AL,AL
- asm jnz slp
- asm mov AX,DI
- asm dec AX
- return (char *) _AX;
- }
-
-
- /* Convert unsigned long 'n' to decimal in string 's' */
- char *ultod(ulong n, char *s)
- {
- /* Load n into BX:AX, s into SI and DI, and the radix into CX */
- asm mov DI,s
- asm mov SI,DI
- asm mov AX,n
- asm mov BX,n+2
- asm mov CX,10
-
- /* Convert n into a digit string, least significant digit first */
- dlp:
- /* Divide BX:AX by CX, quotient to BX:AX, remainder to DX */
- asm xchg AX,BX /* BX = low n */
- asm sub DX,DX /* DX:AX = high n */
- asm div CX /* AX = high q, DX = temporary r */
- asm xchg AX,BX /* BX = high q, DX:AX = temp r:low q */
- asm div CX /* BX:AX = q, DX = r */
- /* Put digit in string */
- asm add DL,'0'
- asm mov [DI],DL
- asm inc DI
- /* Do until BX:AX is zero */
- asm mov DX,AX
- asm or DX,BX
- asm jnz dlp
- /* Terminate string */
- asm mov [DI],AL
-
- /* Reverse the string, putting most significant digit first */
- rlp:
- asm dec DI
- asm cmp DI,SI
- asm jna rfin
- asm mov AL,[SI]
- asm xchg AL,[DI]
- asm mov [SI],AL
- asm inc SI
- asm jmp short rlp
- rfin:
-
- /* Return pointer to start of string */
- asm mov AX,s
- return (char *) _AX;
- }
-
-
- /* Ask user yes/no question */
- int query(char *s, char *t)
- {
- pputs(s);
- pputs(t);
- pputs(" (y/n)? ");
-
- qlp:
- asm mov AH,8 /* Get console input without echo */
- asm int 21h
- asm test AL,AL
- asm jnz got
- asm int 21h /* If extended code, ignore it */
- asm jmp short qlp
- got:
- asm mov DL,AL /* Save character typed */
- asm and AL,5Fh /* Convert lower to upper case */
- asm sub SI,SI /* Return 0 if no */
- asm cmp AL,'N'
- asm je qfin
- asm inc SI /* Return 1 if yes */
- asm cmp AL,'Y'
- asm jne qlp /* Do until we get a proper answer */
- qfin:
-
- pputc(_DX); /* Echo y, n, Y, or N */
- pputs("\r\n");
- return _SI;
- }
-
-
- /* Find first match to 'p', attribute 'a', results in 'f' */
- int fnd1st(char *p, struct find *f, int a)
- {
- /* Set DTA to f */
- asm mov DX,f
- asm mov AH,1Ah
- asm int 21h
-
- /* Do find first */
- asm mov DX,p
- asm mov CX,a
- asm mov AH,4Eh
- asm int 21h
- asm sbb AX,AX
- return _AX;
- }
-
-
- /* Find next match for fnd1st() done on 'f' */
- int fndnxt(struct find *f)
- {
- /* Set DTA to f */
- asm mov DX,f
- asm mov AH,1Ah
- asm int 21h
-
- /* Do find next */
- asm mov AH,4Fh
- asm int 21h
- asm sbb AX,AX
- return _AX;
- }
-
-
- /* Parse file name n into FCB f */
- int parse(char *n, char *f)
- {
- asm mov AX,DS
- asm mov ES,AX
- asm mov SI,n
- asm mov DI,f
- asm mov AX,2901h
- asm int 21h
- asm cbw
- return _AX;
- }
-
-
- /* Get current directory on drive d into string p */
- int curdir(int d, char *p)
- {
- asm mov DL,d
- asm mov SI,p
- asm mov AH,47h
- asm int 21h
- asm jb cerr
- asm sub AX,AX
- cerr:
- return _AX;
- }
-
-
- /* Set new directory p */
- int newdir(char *p)
- {
- asm mov DX,p
- asm mov AH,3Bh
- asm int 21h
- asm jb nerr
- asm sub AX,AX
- nerr:
- return _AX;
- }
-
-
- /* Count the matches for q on drive e (0=default) with attributes */
- /* att/rof and adjust the attribute if needed for the global delete */
- ulong count(int e, char *q)
- {
- int r;
- char *p;
- ulong n;
- struct find d;
- char m[16];
-
- p = m;
- if (e)
- {
- *p++ = e + 'A' - 1;
- *p++ = ':';
- }
- scpy(p, q);
- n = 0;
- r = fnd1st(m, &d, att);
- while (!r)
- {
- if (rof || !(d.attr & 1)) /* Ignore read-only unless specified */
- {
- n++;
- if (d.attr & 7) /* Adjust attribute if needed */
- {
- scpy(p, d.name);
- asm lea DX,m
- asm sub CX,CX
- asm mov AX,4301h
- asm int 21h
- }
- }
- r = fndnxt(&d);
- }
- return n;
- }
-
-
- void remove(char *a)
- {
- register char *p, *q;
- int e;
- struct find d;
- char s[128];
- char t[128];
-
- /* Setup */
- q = s;
- p = scpy(q, a) - 1; /* Copy path being searched */
- while (p >= q && *p != '\\' && *p != ':')
- p--; /* Scan back for path delimiter */
- p++; /* Point past delimiter */
- if (sub || fin)
- scpy(t, p); /* Save name to match */
-
- /* First try global delete since it is faster */
- if (!ask) /* Of course, skip if prompting */
- do { /* (use "do" so can use "break") */
- char *r, f[44], u[128], v[128];
-
- /* Set up to do global delete using FCB calls */
- f[0] = 0; /* Not an extended FCB */
- if (parse(p, f+7) != 1) /* Make an FCB out of the name */
- break; /* If not global, skip all this */
- if (q[0] && q[1] == ':') /* Get drive */
- e = (q[0] & 0x5f) - 'A' + 1;
- else
- e = 0; /* Use default drive */
- f[7] = e; /* Set drive */
- *v = '\0'; /* No current path yet */
- if (p - q && *(p-1) != ':') /* Change path if needed */
- {
- r = v; /* Construct old path */
- if (e)
- {
- *r++ = e + 'A' - 1; /* Put in drive if needed, */
- *r++ = ':';
- }
- *r++ = '\\'; /* and backslash to start at root */
- if (curdir(e, r)) /* Get current directory */
- break; /* If can't get it, skip rest */
- scpy(u, q); /* Copy and terminate new path */
- u[p - q - (p - q != 1 && u[p - q - 1] != ':')] = '\0';
- if (newdir(u))
- break; /* If no such path, skip rest */
- }
-
- /* Do global delete and count how many files were deleted */
- fls += count(e, p); /* Adjust attributes if neeeded */
- asm lea DX,f+7
- asm mov AH,13h
- asm int 21h
- fls -= count(e, p);
-
- /* Change path on that drive back to what it was */
- if (*v)
- newdir(v);
- } while (0); /* (not really a loop) */
-
- /* Find matching files */
- e = fnd1st(q, &d, att); /* Search allowed attributes */
- while (!e)
- {
- if (rof || !(d.attr & 1)) /* Ignore read-only unless specified */
- {
- scpy(p, d.name);
- if (!ask || query("Delete file ", strlow(q)))
- {
- if (d.attr & 1) /* If read-only and got here, */
- { /* then change the attribute */
- asm mov DX,q
- asm sub CX,CX
- asm mov AX,4301h
- asm int 21h
- }
- asm mov DX,q /* Delete file */
- asm mov AH,41h
- asm int 21h
- asm jnb delok
- pputs("Could not delete file ");
- pputs(strlow(q));
- pputs("\r\n");
- asm jmp short delbad
- delok:
- fls++;
- delbad: ;
- }
- }
- e = fndnxt(&d);
- }
-
- /* Find matching subdirectories, if requested */
- if (sub || fin)
- {
- scpy(p, fin ? "*.*" : t); /* What directories to match */
- e = fnd1st(q, &d, 0x17); /* Look at everything except labels */
- while (!e) /* Do all subdirectories */
- {
- if ((d.attr & 0x10) &&
- (d.name[0] != '.' || (d.name[1] && d.name[1] != '.')))
- { /* Is subdir and not "." or ".." */
- q = scpy(p, d.name); /* Overlay wildcard with found name */
- *q++ = '\\'; /* Append path delimiter */
- scpy(q, fin ? t : "*.*"); /* New wildcard */
- remove(s); /* Do subdirectory */
- if (!fin) /* Remove directory if not in find */
- {
- *--q = '\0';
- if (!ask || query("Remove directory ", strlow(s)))
- {
- asm lea DX,s /* Remove directory */
- asm mov AH,3Ah
- asm int 21h
- asm jnb rdok
- e = _AX; /* Save error code */
- pputs("Could not remove directory ");
- pputs(strlow(s));
- if (e == 0x10) /* See if current directory */
- pputs(" - part of current path");
- else
- pputs(" - unable to empty it");
- pputs("\r\n");
- asm jmp short rdbad
- rdok:
- drs++;
- rdbad: ;
- }
- }
- }
- e = fndnxt(&d);
- }
- }
- }
-
-
- void main(int argc, char *argv[])
- {
- register char *p;
- register int k;
- int i, m;
- char *a[64]; /* Tokens and options */
- char t[64]; /* Token/~option flags */
-
-
- /* Parse line into tokens and options */
- m = k = 0;
- for (i = 1; i < argc; i++) /* Do command line */
- {
- p = argv[i]; /* Next argument */
- if (*p != '/') /* See if token */
- {
- a[k] = p++;
- t[k++] = 1; /* Token */
- m = 1; /* Token flag */
- while (*p && *p != '/')
- p++;
- if (*p) /* See if options on token */
- {
- *p++ = '\0'; /* Terminate token string */
- a[k] = a[k-1]; /* Put options appended to token */
- t[k] = 1; /* BEFORE that token. */
- a[k-1] = p;
- t[k-1] = 0; /* Option(s) */
- k++;
- }
- }
- else
- {
- a[k] = ++p; /* Options start after the slash */
- t[k++] = 0; /* Option(s) */
- if (m)
- m = -1; /* Dangling option flag */
- }
- }
- if (m < 0)
- {
- pputs("Dangling option---entire command ignored\r\n");
- asm mov AX,4C01h /* Exit with error */
- asm int 21h
- }
- m = k; /* Number of tokens and options */
-
- /* Process tokens and options */
- fls = drs = 0; /* Initialize files, dirs removed */
- for (i = 0; i < m; i++)
- if (t[i]) /* Token */
- remove(a[i]);
- else /* Options */
- for (p = a[i]; (k = *p) != 0; p++)
- if (k == '/') {} /* Ignore extra /'s */
- else if ((k &= 0x5f) == 'Q') ask = 0; /* Don't prompt */
- else if (k == 'P') ask = 1; /* Prompt */
- else if (k == 'N') sub = 0; /* No subdirs */
- else if (k == 'S') sub = 1; /* Subdirs */
- else if (k == 'V') att &= ~2; /* Ignore hidden */
- else if (k == 'H') att |= 2; /* Delete hidden */
- else if (k == 'T') att &= ~4; /* Ignore system */
- else if (k == 'Y') att |= 4; /* Delete system */
- else if (k == 'W') rof = 0; /* Ignore read-only */
- else if (k == 'R') rof = 1; /* Delete read-only */
- else if (k == 'A') fin = 0; /* Use *.* in subs */
- else if (k == 'F') fin = 1; /* Find file */
- else /* Invalid option */
- {
- if (*p != '?')
- pputs("Invalid option\r\n");
- pputs("\
- RM 1.4 Copyright (C) 1988,1989 Mark Adler All rights reserved.\r\n\
- Valid options are (*=default):\r\n\
- /P\t- Prompt for each deletion\r\n\
- /Q\t- No prompting *\r\n\
- /S\t- Remove subdirectories\r\n\
- /N\t- Ignore subdirectories *\r\n\
- /H\t- Delete hidden files\r\n\
- /V\t- Ignore hidden files *\r\n\
- /Y\t- Delete system files\r\n\
- /T\t- Ignore system files *\r\n\
- /R\t- Delete read-only files!\r\n\
- /W\t- Ignore read-only files *\r\n\
- /F\t- Find files\r\n\
- /A\t- All files *\r\n\
- /?\t- Display this list\r\n");
- asm mov AX,4C01h /* Exit with error */
- asm int 21h
- }
- pputs(ultod(fls, t));
- pputs(" file");
- if (fls != 1)
- pputc('s');
- pputs(" deleted");
- if (drs)
- {
- pputs(" and ");
- pputs(ultod(drs, t));
- pputs(" director");
- if (drs != 1)
- pputs("ies");
- else
- pputc('y');
- pputs(" removed");
- }
- pputs("\r\n");
- }